package de.uni_potsdam.hpi.bpt.ai.example;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.json.JSONException;

import de.uni_potsdam.hpi.bpt.ai.collection.BPMAIExport;
import de.uni_potsdam.hpi.bpt.ai.collection.BPMAIExportBuilder;
import de.uni_potsdam.hpi.bpt.ai.collection.Model;
import de.uni_potsdam.hpi.bpt.ai.collection.Revision;
import de.uni_potsdam.hpi.bpt.ai.diagram.Diagram;
import de.uni_potsdam.hpi.bpt.ai.diagram.Shape;

public class ElementClusters {
	
	
	public static Map<String, Set<String>> iterate(String namespace) throws UnsupportedEncodingException, JSONException, IOException {

		Map<String, Set<String>> data = new HashMap<String, Set<String>>();
		
		File rootDir = new File(Experiment.MODEL_PATH);
		
		if (!rootDir.exists()) {
			throw new FileNotFoundException("root directory " + Experiment.MODEL_PATH + " does not exist");
		}

		if (!rootDir.isDirectory()) {
			throw new FileNotFoundException("root directory " + Experiment.MODEL_PATH + " is not a directory");
		}
		
		// parse directory
		BPMAIExport ex = BPMAIExportBuilder.parseDirectory(rootDir);
		Set<Model> models = ex.getModels();
		int c = 0;
		
		for (Model m : models) {
			c++;
			
			Revision last = null;
			for (Revision r : m.getRevisions()) {
				last = r;
			}
			
			Diagram diagram = last.getDiagram();
			
			if (namespace.equals(diagram.getStencilset().getNamespace())) {
				examineModel(data, diagram, m.getId());
			
//			String[] ns = namespace.split("/");
//			System.out.print(String.format("%5d/%5d - %-18s | %8s | ", 
//					++c, 
//					models.size(), 
//					(m.getName().length() > 18 ? (m.getName().trim().substring(0, Math.min(15, m.getName().length())) + "...") : m.getName()),
//					ns[ns.length-1]));
			}
			
		}
		
		 
		return data;
		
	}
	
	public static void examineModel(Map<String, Set<String>> data, Diagram diagram, String modelId) {
		
		for (Shape shape : diagram.getShapes()) {
			String sid = shape.getStencilId();
			
			if (!data.containsKey(sid)) {
				data.put(sid, new HashSet<String>());
			}
			
			if (!data.get(sid).contains(modelId)) {
				data.get(sid).add(modelId);
			}
		}
	}
	
	protected static Map<List<String>, Set<String>> calculateClusters(final Map<String, Set<String>> data) {

		// a comparator based on the number of occurences in the data -- high first
		Comparator<String> compare = new Comparator<String>() {
			public int compare(String o1, String o2) {
				return -1 * ((Integer)data.get(o1).size()).compareTo(data.get(o2).size());
			}
		};
		
		List<String> keyset = new ArrayList<String>(data.keySet());
		Collections.sort(keyset, compare);
		
		// max number of combination (cluster depth)
		int maxDepth = 10;
		
		// create combinations
		List<String> keys = keyset.subList(0, maxDepth);
		Set<List<String>> combinations = new HashSet<List<String>>();
		combinations.add(new ArrayList<String>());
		while(!keys.isEmpty()) {
			Set<List<String>> news = new HashSet<List<String>>();
			for (List<String> c : combinations) {
				for (String k : keys) {
					if (!c.contains(k)) {
						List<String> new_c = new ArrayList<String>(c);
						new_c.add(k);
						news.add(new_c);
					}
				}
			}

			combinations.addAll(news);
			keys.remove(0);
		}
		
		System.out.println("Created " + combinations.size() + "combinations");
		
//		for (Set<String> c : combinations) {
//			for (String s : c) {
//				System.out.print(s+".");
//			}
//			System.out.println();
//		}

		// returns the number of models that contain all element types in keys
		
		Map<List<String>, Set<String>> result = new HashMap<List<String>, Set<String>>();
		
		for (List<String> c : combinations) {
			List<String> index = new ArrayList<String>(c);
			
			if (c.size() == 0) {
				continue;
			}
			
			Set<String> models = new HashSet<String>(data.get(c.get(0)));
			Set<String> toRemove = new HashSet<String>();
			for (String key : c) {
				for (String mid : models) {
					if (!data.get(key).contains(mid)) {
						toRemove.add(mid);
					}
				}
				
				for (String r : toRemove) {
					models.remove(r);
				}
					
				result.put(index, models);
			}
		}
		return result;
	}
	
	public static void main(String[] args) throws UnsupportedEncodingException, JSONException, IOException {
		
		//Map<String, Integer> cluster = calculateClusters(iterate(NS_BPMN));
		
		Map<String, Set<String>> raw = iterate(Experiment.NS_BPMN);
		Map<List<String>, Set<String>> clusters = calculateClusters(raw);
		printCluster(clusters, raw);
		
	}
	
	protected static void printCluster(final Map<List<String>, Set<String>> clusters, final Map<String, Set<String>> raw) throws IOException {
		Comparator<List<String>> compare = new Comparator<List<String>>() {
			public int compare(List<String> o1, List<String> o2) {
				return -1 * ((Integer)clusters.get(o1).size()).compareTo(clusters.get(o2).size());
			}
		};
		
		List<List<String>> sort = new ArrayList<List<String>>(clusters.keySet());
		Collections.sort(sort, compare);
		
		//sort = sort.subList(0, 1000);
		
		Comparator<String> compare2 = new Comparator<String>() {
			public int compare(String o1, String o2) {
				return -1 * ((Integer)raw.get(o1).size()).compareTo(raw.get(o2).size());
			}
		};
		
		// open file
		FileWriter outFile = new FileWriter(Experiment.STAT_PATH + File.separator + "cluster.txt");
		PrintWriter out = new PrintWriter(outFile);
		
		Set<String> labels = new HashSet<String>();
		
		for(List<String> c : sort) {
			Collections.sort(c, compare2);
			String label = "";
			for (String l : c) {
				label += l + "-";
			}
			if (!labels.contains(label)) {
				out.println(String.format("%5d | %s", clusters.get(c).size(), label));
				out.flush();
				labels.add(label);
			}
		}
		
		out.close();
	}
	
	protected static void printResults(final Map<String, Set<Long>> data) {
		Comparator<String> compare = new Comparator<String>() {
			public int compare(String o1, String o2) {
				return -1 * ((Integer)data.get(o1).size()).compareTo(data.get(o2).size());
			}
		};
		
		List<String> keyset = new ArrayList<String>(data.keySet());
		Collections.sort(keyset, compare);
		
		for (String key: keyset) {
			System.out.println(String.format("%45s | %5d", key, data.get(key).size()));
		}		
	}
}
